#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <vector>
#include <sstream>

#include <sys/types.h> //socket网络文件创建
#include <sys/socket.h>
#include <arpa/inet.h> //htonl()
#include <netinet/in.h> //sockaddr
#include <pthread.h>

#include <strings.h> //void bzero()

#include <cerrno> //错误码
#include <cstring> //错误码描述
#include <cstdlib> //exit
#include <cstdio> //sprintf

#include "err.hpp" //错误码归纳
#include "mymutex.hpp" //类似unique_lock的锁
#include "mythread.hpp" //模拟实现thread库
#include "RingQueue.hpp" //生产消费循环队列

void func() {}

namespace my_server
{
    class Server
    {
    //     typedef std::function<std::string(std::string)> func_t; //处理函数
    // public:
    //     Server(func_t func,int port = 8080)
    //         :_port(port)
    //         ,_sock(0)
    //         ,_func(func)
    //     {
    //         //std::cout<<"server port:"<< _port <<std::endl;

    //     }

        // void InitServer() //初始化服务器
        // {
        //     //1.创建网络文件
        //     _sock = socket(AF_INET,SOCK_DGRAM,0); //SOCK_DGRAM数据报方式传递(UDP) 网络模式
        //     if(_sock < 0)
        //     {
        //         std::cout<<"create socket error: "<< strerror(errno) << std::endl;
        //         exit(SOCK_ERR);
        //     }
        //     //std::cout<<"socket:"<<_sock<<std::endl; //正常情况下是3


        //     //2.打包写入ip和端口 网络信息 让别人能够找到文件并访问
        //     struct sockaddr_in local;
        //     bzero(&local,sizeof(local)); //将对象中的空间全部置为0  填充字也置为0
        //     local.sin_family = AF_INET; //模式 或PF_INET  以网络的方式
        //     local.sin_port = htons(_port); //端口 转换大小端 网络和本地格式
        //     local.sin_addr.s_addr =  INADDR_ANY; //IP地址 默认0.0.0.0 分配任意IP

        //     if(bind(_sock,(struct sockaddr*)(&local), sizeof(local)) < 0) //开始绑定信息 - 相对于加上报头
        //     {
        //         std::cout<<"bind error:"<<strerror(errno)<<std::endl;
        //         exit(BIND_ERR);
        //     }
        //     //std::cout<<"bind the success"<<std::endl;
        // }

        // //启动服务器start 四种运行模式
        // //1.发什么返回什么
        // //2.发给服务器，服务器处理再返回(服务器处理函数的引入) 将输入的小写字母转大写 大写转小写
        // //3.发送命令，服务器处理并返回命令
        // //4.生产者消费者 多线程收发

        // void start()  //  1 2 3
        // {
        //     while(true)
        //     {
        //         const int size = 1024;
        //         char buf[size] = {0};
        //         struct sockaddr_in local;
        //         bzero(&local,sizeof(local));
        //         socklen_t locallen = (unsigned int)sizeof(local);
        //         int ret = recvfrom(_sock,buf,size-1,0,(struct sockaddr*)(&local),&locallen); //接收数据 阻塞式发送

                
        //         if(ret == -1)
        //         {
        //             std::cout<<"recvfrom error:"<<strerror(errno)<<std::endl;
        //             exit(REVCFROM_ERR);
        //         }
        //         else if(ret == 0) continue;

        //         buf[ret] = '\0'; //读取的字符串设置\0
        //         const char* sockip = inet_ntoa(local.sin_addr); //获取发送者IP
        //         const uint16_t sockport = ntohs(local.sin_port); //获取发送者端口
        //         std::cout<<sockip<<"|"<<sockport<<": "<<buf<<std::endl; //显示接收到的信息

        //         //bzero(&buf,sizeof(buf)); //清空字符串
        //         //sprintf(buf,"成功接收!"); //回应

        //         std::string str = _func(buf); //大写转小写 小写转大写 再发送

        //         //ret = sendto(_sock,buf,size-1,0,(struct sockaddr*)(&local),sizeof(local)); //发送数据 阻塞式发送
        //         ret = sendto(_sock,str.c_str(),str.size(),0,(struct sockaddr*)(&local),sizeof(local)); //发送数据 阻塞式发送

        //         if(ret == -1)
        //         {
        //             std::cout<<"sendto error:"<<strerror(errno)<<std::endl;
        //             exit(SENDTO_ERR);
        //         }
        //     }
        // }

    public:
        Server(int port = 8080)
            :_port(port)
            ,_sock(0)
            ,_c(nullptr)
            ,_p(nullptr)
        {
            pthread_mutex_init(&_m,nullptr); //初始化锁

            _p = new Thread("P",std::bind(&Server::Recv,this));
            _c = new Thread("C",std::bind(&Server::Broadcast,this));
        }

        void start() // 4 初始化服务器 并开启线程
        {
            //1.创建网络文件
            _sock = socket(AF_INET,SOCK_DGRAM,0); //SOCK_DGRAM数据报方式传递(UDP) 网络模式
            if(_sock < 0)
            {
                std::cout<<"create socket error: "<< strerror(errno) << std::endl;
                exit(SOCK_ERR);
            }
            //std::cout<<"socket:"<<_sock<<std::endl; //正常情况下是3


            //2.打包写入ip和端口 网络信息 让别人能够找到文件并访问
            struct sockaddr_in local;
            bzero(&local,sizeof(local)); //将对象中的空间全部置为0  填充字也置为0
            local.sin_family = AF_INET; //模式 或PF_INET  以网络的方式
            local.sin_port = htons(_port); //端口 转换大小端 网络和本地格式
            local.sin_addr.s_addr =  INADDR_ANY; //IP地址 默认0.0.0.0 分配任意IP

            if(bind(_sock,(struct sockaddr*)(&local), sizeof(local)) < 0) //开始绑定信息 - 相对于加上报头
            {
                std::cout<<"bind error:"<<strerror(errno)<<std::endl;
                exit(BIND_ERR);
            }
            //std::cout<<"bind the success"<<std::endl;

            //启动线程 服务器正式开始运行
            _p->run();
            _c->run();
        }

protected:

        //ip+port-sockaddr
        void AddUser(const std::string& username,const sockaddr_in& usersock)
        {
            if(_contacts.count(username)) return; //检查用户是否已存在
            _contacts[username] = usersock;
            std::cout<<username<<"加入群聊!"<<std::endl;
        }

        void Recv() //读取线程函数
        {
            while(true)
            {
                const int size = 1024;
                char buf[size] = {0};
                struct sockaddr_in local;
                bzero(&local,sizeof(local));
                socklen_t locallen = (unsigned int)sizeof(local);
                int ret = recvfrom(_sock,buf,size-1,0,(struct sockaddr*)(&local),&locallen); //接收数据 阻塞式发送
                if(ret == -1)
                {
                    std::cout<<"recvfrom error:"<<strerror(errno)<<std::endl;
                    exit(REVCFROM_ERR);
                }
                else if(ret == 0) continue;

                buf[ret] = '\0';
                std::string username("[");
                username += inet_ntoa(local.sin_addr); //获取ip+port
                username += "|";
                std::stringstream ss;
                ss<<ntohs(local.sin_port);
                username += ss.str();
                username += "]";
                
                AddUser(username,local);
                _rq.push(username+": "+buf); //将信息丢入循环队列
            }
        }

        void Broadcast() //广播线程 向所有用户发送消息
        {
            while(true)
            {
                std::string str;
                _rq.pop(&str);

                std::vector<struct sockaddr_in> _user;

                //临界区
                {
                    lock lk(&_m); //上锁
                    for(const auto& u:_contacts) _user.push_back(u.second);
                }

                for(auto&u : _user) //取出每一个用户进行广播
                {
                    int ret = sendto(_sock,str.c_str(),str.size(),0,(struct sockaddr*)(&u),sizeof(u)); //发送数据 阻塞式发送
                    if(ret == -1) //如果发送失败 默认用户退出 删除用户 - 如果存在效率问题可以考虑关闭该功能
                    {   
                        std::string username("[");
                        username += inet_ntoa(u.sin_addr); //获取ip+port
                        username += "|";
                        std::stringstream ss;
                        ss<<ntohs(u.sin_port);
                        username += ss.str();
                        username += "]";
                        //临界区删除
                        {
                            lock lk(&_m);
                            _contacts.erase(username); //删除该用户
                            std::cout<<"delete user->"<<username<<std::endl;
                        }
                    }
                    //else std::cout<<"sentto done..."<<std::endl; //如果发送成功给予回应 - 默认关闭
                }

            }

        }

public:
        ~Server()
        {
            pthread_mutex_destroy(&_m);
            
            _p->join();
            _c->join();

            delete _p;
            delete _c;
        }

    private:
        //真实情况下 一个服务器有许多个网卡 多个IP 为了灵活运用 设置为随机分配IP
        //std::string _ip;

        int _port; //端口
        int _sock; //套接字文件描述符

        //通讯录 每连接一个client就记录下来 name=ip+port 
        std::unordered_map<std::string,struct sockaddr_in> _contacts;
        pthread_mutex_t _m; //锁
        RingQueue<std::string> _rq; //消息队列

        Thread* _p; //生产者-接收消息
        Thread* _c; //消费者-发送消息(广播)

        //func_t _func; //数据处理函数
    };
}